package com.jplus.core.utill.clazz;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 用于获取类的模板类
 */
public abstract class ClassTemplate<T> {

	private static final Logger logger = LoggerFactory.getLogger(ClassTemplate.class);

	public final List<T> getFileList(String packageName) {
		List<T> list = new ArrayList<T>();
		try {
			// 从包名获取 URL 类型的资源
			Enumeration<URL> urls = ClassUtil.getClassLoader().getResources(packageName.replace(".", "/"));
			// 遍历 URL 资源
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				list.addAll(find(url, packageName));
			}
		} catch (Exception e) {
			logger.error("获取类出错！", e);
		}
		return list;
	}

	public final T getFile(String fileName) {
		try {
			URL url = ClassUtil.getClassLoader().getResource(fileName);
//			logger.info("URL:" + url);
			List<T> list = find(url, "");
			if (!list.isEmpty())
				return list.get(0);
		} catch (Exception e) {
			logger.error("获取文件出错！", e);
		}
		return null;
	}

	private List<T> find(URL url, String packageName) throws IOException, URISyntaxException {
		List<T> fileList = new ArrayList<T>();
		String packagePath = packageName.replaceAll("\\.", "/");
		if (url != null) {
			// 获取协议名（分为 file 与 jar）
			String protocol = url.getProtocol();
			if (protocol.equals("file")) {
				// 若在 class 目录中，则执行添加类操作
				// String packagePath = url.getPath().replaceAll("%20", " ");
				addFile(fileList, url.getFile(), packageName);
			} else if (protocol.equals("jar")) {
				// 若在 jar 包中，则解析 jar 包中的 entry
				JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
				JarFile jarFile = jarURLConnection.getJarFile();
				Enumeration<JarEntry> jarEntries = jarFile.entries();
				while (jarEntries.hasMoreElements()) {
					JarEntry jarEntry = jarEntries.nextElement();
					String file = jarEntry.getName();
//					logger.warn("#" + file + "\t" + packagePath);
//					logger.warn("#" + file.contains(packagePath));
					if (file.contains(packagePath))
						if (file.contains(".") && !file.endsWith("/") && !file.endsWith(".")
								&& !file.endsWith(File.separator)) {
							// logger.info(file);
							String suffix = file.substring(file.lastIndexOf("."));
							String fileName = file.substring(file.lastIndexOf("/") + 1,
									file.length() - suffix.length());
							String filePkg = file.indexOf("/") > 0
									? file.substring(0, file.length() - fileName.length() - suffix.length() - 1)
											.replaceAll("/", ".")
									: "";
							// 执行添加类操作
							T t = doAdd(filePkg, fileName, suffix);
							if (t != null)
								fileList.add(t);
						}
				}
			}
		}
		return fileList;
	}

	private void addFile(List<T> fileList, String path, String packageName) {
		try {
			File file = new File(path);
			if (file.isDirectory()) {
				for (File t : file.listFiles()) {
					if (t.isDirectory())
						addFile(fileList, t.getPath(), packageName + "." + t.getName());
					else
						addFile(fileList, t.getPath(), packageName);
				}
			} else {
				String fileName = file.getName();
				String className = fileName.substring(0, fileName.lastIndexOf("."));
				T t = doAdd(packageName, className, fileName.substring(fileName.lastIndexOf("."), fileName.length()));
				if (t != null)
					fileList.add(t);
			}
		} catch (Exception e) {
			logger.error("添加类出错！", e);
		}
	}

	private T doAdd(String packageName, String fileName, String suffix) {
		// 忽略部分代码，无需加载
		if (packageName.indexOf("junit") >= 0)
			return null;
		// 判断是否可以添加类
		return checkAndAdd(packageName, fileName, suffix);
	}

	/**
	 * 验证是否允许添加类
	 */
	public abstract T checkAndAdd(String packageName, String fileName, String suffix);

}